/**
 * @file   Form.java
 * @author Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Sat Sep 29 08:24:36 2007
 * 
 * @brief  
 * 
 * 
 */

package sim.forms;

import java.util.*;

import sim.framework.*;
import sim.framework.io.*;
import sim.framework.dialog.*;
import sim.utils.parser.*;
import sim.utils.Utils;


public final class Form implements FileGets, ParserScan {

    // Datos
    private Simlet m_Simlet = null;
    private Vector m_Nodes = new Vector(4);
    private FrmNode m_Pivot = null;

    // Evaluador de expresiones.
    private FrmEval m_Eval = new FrmEval();
    
    private Form(Simlet s) {
	m_Simlet = s;
    }

    public static Form create(Simlet s, final String filename) {
	Form frm = null;
	frm = new Form(s);
	if (frm != null) {
	    if (!frm.load(filename)) {
		frm = null;
	    }
	}
	return frm;
    }

    public boolean load(final String filename) {
	FileStream file;
	// Borramos los nodos presentes
	m_Nodes.removeAllElements();
	file = (FileStream)FileStream.create(m_Simlet,
					     filename,
					     File.MODE_READ);
	if (file != null) {
	    // Leer el archivo linea por linea
	    m_Pivot = null;
	    file.gets(this);
	    // Cerrar el archivo.
	    file.close();
	}
	return (m_Nodes.size() > 0);
    }

    /** 
     * Establece el valor de cada nodo de un formulario.
     *
     * Esta función es util cuando los valores provienen de un archivo.
     * 
     * @param line Arreglo de bytes que contiene el valor de cada nodo separado por FrmNode::TOKEN.
     * @param start Primer byte válido del arreglo de bytes.
     * @param count Cantidad de bytes válidos del arreglo.
     */
    public final void setOptions(final byte[] line, int start, int count) {
	Parser p = new Parser(this, FrmNode.TOKEN, Parser.FORWARD);
	m_Pivot = null;
	p.scan(line, start, count);
    }

    /** 
     * Establece el valor de cada nodo de un formulario.
     * 
     * @param line Arreglo de bytes que contiene el valor de cada nodo separado por FrmNode::TOKEN.
     */
    public final void setOptions(final byte[] line) {
	setOptions(line, 0, line.length);
    }
        
    /** 
     * Establece el valor de cada nodo de un formulario.
     *
     * Esta función es util cuando los valores se mantienen en memoria.
     * 
     * @param line Cadena de caracteres que contiene el valor de cada nodo
     *             separado por FrmNode::STR_TOKEN.
     */
    public final void setOptions(final String line) {
	byte[] bytesNode;
	bytesNode = Utils.encoding(line, m_Simlet.getAppEncoding());
	setOptions(bytesNode, 0, bytesNode.length);
    }

    /** 
     * Devuelve el valor asociado a un nodo.
     *
     * @param nodeId Identificador del nodo.
     * 
     * @return Devuelve una cadena de caracteres que representa en contenido
     *         ingresado en el control asociado.
     */
    public final String getOption(int nodeId) {
	Enumeration e;
	FrmNode node;
	e = m_Nodes.elements();
	while(e.hasMoreElements()) {
	    node = (FrmNode)e.nextElement();
	    if (node.m_Node == nodeId) {
		return node.getOption();
	    }	    
	}
	return null;
    }

    /**
     * IMPLEMENTACIÓN DE INTERFACES.
     * 
     */

    public boolean fileGetsLine(final byte[] line, int size, int crlf) {
	FrmNode newNode = null;
	FrmItem newItem = null;

	//#ifdef DEBUG_FORM
	//# System.out.println("Form::fileGetsLineLoadForm() ENTERED");
	//#endif

	//#ifdef DEBUG_FORM
	//# System.out.println("\tline: " + Utils.decoding(line, 0, size - crlf, m_Simlet.getAppEncoding()));
	//#endif

	if (size > crlf) {
	    // Si la linea le corresponde a un nuevo nodo ...
	    newNode = FrmNode.create(m_Simlet.getAppEncoding(), line, 0, size - crlf);
	    if (newNode != null) {
		// Añadir el nuevo nodo
		//#ifdef DEBUG_FORM
		//# System.out.println("\tNuevo nodo creado!.");
		//# System.out.println("\tnewNode.m_Node = " + newNode.m_Node);
		//# System.out.println("\tnewNode.m_Type = " + newNode.m_Type);
		//# System.out.println("\tnewNode.m_Seek = " + newNode.m_Seek);
		//# System.out.println("\tnewNode.m_Oper = " + newNode.m_Oper);
		//# System.out.println("\tnewNode.m_Info = " + newNode.m_Info);
		//# System.out.println("\tnewNode.m_File = " + newNode.m_File);
		//#endif
		m_Nodes.addElement(newNode);
		// ... Cambiar el nodo actual.
		m_Pivot = newNode;
	    }
	
	    // Si la linea no es un nuevo nodo, es probable que sea un item del actual nodo.
	    if ((newNode == null) && (m_Pivot != null)) {
		newItem = FrmItem.create(m_Simlet.getAppEncoding(), m_Pivot,
					 line, 0, size - crlf);
		if (newItem != null) {
		    //#ifdef DEBUG_FORM
		    //# System.out.println("\tNuevo item creado para nodo : " + m_Pivot.m_Node);
		    //# System.out.println("\tFrmItem.m_Code = " + newItem.m_Code);
		    //# System.out.println("\tFrmItem.m_Desc = " + newItem.m_Desc);
		    //# System.out.println("\tFrmItem.m_Node = " + newItem.m_Node);
		    //#endif
		    // ... añadir el item al actual nodo.
		    m_Pivot.appendItem(newItem);
		}
	    }
	}
	
	//#ifdef DEBUG_FORM
	//# System.out.println("Form::fileGetsLineLoadForm() EXITING");
	//#endif
	
	return true;
    }
    
    public boolean parserHit(int hit, final byte[] line, int start, int length, Object something) {
	FrmNode node;
	m_Pivot = nextNode(m_Pivot);
	if (m_Pivot != null) {
	    m_Pivot.setOption(line, start, length);
	}
	return true;
    }

    public boolean parserEnd(boolean cancel) {
	return true;
    }

    protected FrmNode nextNode(FrmNode curNode) {
	int nodeID;

	// Cargar el nodo
	if (curNode == null) {
	    curNode = (FrmNode)m_Nodes.firstElement();
	}
	else {
	    nodeID = curNode.gotoNode(m_Eval);
	    switch(nodeID) {
	    case FrmNode.NODE_EXIT:
		curNode = null;
		break;
	    case FrmNode.NODE_NEXT:
		curNode = selectNode(curNode.m_Node, true);
		break;
	    default:
		curNode = selectNode(nodeID, false);
	    }
	}
	return curNode;
    }

    private FrmNode selectNode(int nodeID, boolean next) {
	Enumeration e;
	FrmNode ret;
	ret = null;
	e = m_Nodes.elements();
	while(e.hasMoreElements()) {
	    ret = (FrmNode)e.nextElement();
	    if (ret.m_Node == nodeID) {
		if (next) {
		    ret = (e.hasMoreElements() ? (FrmNode)e.nextElement() : null);
		}
		break;
	    }
	    ret = null;
	}
	return ret;
    }
}
